﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Starfire3d.Interfaces;

namespace Starfire3d.Data.Objects
{
    public class UnitDetails : ICloneable
    {
        #region Attributes
        private List<ShipSystem> sysList = new List<ShipSystem>();

        private double hull = 0;
        private double cost = 0.0;
        private int maxSpeed = 0;
        private int turnMode = 1;

        private int engineSize = 1;
        #endregion

        #region Public Properties
        public List<ShipSystem> Systems
        {
            get { return sysList; }
        }
        public double Hull
        {
            get { return hull; }
        }
        public double Cost
        {
            get { return cost; }
        }
        public int MaxSpeed
        {
            get { return maxSpeed; }
        }
        public int TurnMode
        {
            get { return turnMode; }
        }

        public List<WeaponSystem> Weapons
        {
            get { return sysList.OfType<WeaponSystem>().Where(sys => !sys.IsDestroyed).ToList(); }
        }
        public bool HasSensors()
        {
            return sysList.Any(sys => sys.IsSensor && !sys.IsDestroyed);
        }
        public bool HasLifeSupport()
        {
            return sysList.Any(sys => sys.IsLifeSupport && !sys.IsDestroyed);
        }
        #endregion

        public UnitDetails()
        {
        }

        public int UndamagedSystems()
        {
            return sysList.Where(sys => !sys.IsDestroyed).Count();
        }

        public int MaxRange()
        {
            List<WeaponSystem> weapons = Weapons;
            if (weapons.Count == 0)
                return 0;

            int range = 0;

            foreach (WeaponSystem w in weapons)
            {
                if( w.MaxRange() > range)
                    range = w.MaxRange();
            }

            return range;
        }

        public int BestRange()
        {
            List<WeaponSystem> weapons = Weapons;
            if (weapons.Count == 0)
                return 0;

            int range = 0;

            foreach (WeaponSystem w in weapons)
            {
                range += w.MaxBestRange();
            }

            return range / weapons.Count();
        }

        public void AddSystem(ShipSystem system, ShipClass shipClass)
        {
            sysList.Add(system);

            hull += system.Size;
            cost += system.Cost;

            if (system.IsEngine)
            {
                int numEngines = sysList.Count(sys => sys.IsEngine);
                maxSpeed = numEngines / shipClass.EngineSize;

                turnMode = shipClass.TurnMode;
                engineSize = shipClass.EngineSize;
            }
        }

        public void ApplyDamage(int damageDone, WeaponSystem weaponType)
        {
            List<ShipSystem> nonDamagedSystems = sysList.Where(sys => !sys.IsDestroyed).Cast<ShipSystem>().ToList();

            if (nonDamagedSystems.Count <= 0)
                return;

            int finalDmg = damageDone;

            if (weaponType.Data.GetBool("TRIPLEVSNODF") && !sysList.Any(sys=> sys.IsEngine && !sys.IsDestroyed))
                finalDmg *= 3;

            if (weaponType.Data.GetBool("CUTTING"))
            {
                if (nonDamagedSystems[0].Data.GetBool("SHIELD") || nonDamagedSystems[0].Data.GetBool("ARMOUR"))
                    finalDmg /= 2;
                else
                    finalDmg *= 2;
            }

            if (weaponType.Data.GetBool("ENERGY"))
            {
                if (nonDamagedSystems[0].Data.GetBool("SHIELD"))
                    finalDmg /= 2;
            }

            int d = 0;
            int i = 0;
            bool skip = false;
            while( d < finalDmg)
            {
                skip = false;

                if (weaponType.Data.GetBool("ENERGY") ||
                    weaponType.Data.GetBool("NEEDLE"))
                {
                    if (nonDamagedSystems[i].Data.GetBool("ARMOUR") || nonDamagedSystems[i].Data.GetBool("HOLD"))
                        skip = true;
                }

                if (!skip && weaponType.Data.GetBool("PRIMARY") ||
                    weaponType.Data.GetBool("PRECISIONLASER"))
                {
                    if (nonDamagedSystems[i].Data.GetBool("SHIELD") || nonDamagedSystems[i].Data.GetBool("ARMOUR"))
                        skip = true;
                }

                if (!skip && weaponType.Data.GetBool("LASER"))
                {
                    if (nonDamagedSystems[i].Data.GetBool("SHIELD"))
                        skip = true;
                }

                if (!skip)
                {
                    nonDamagedSystems[i].IsDestroyed = true;
                    ++d;

                    if (nonDamagedSystems[i].IsEngine)
                        DamageEngine();

                    if (!sysList.Any(sys => !sys.Data.GetBool("SHIELD") && !sys.IsDestroyed))
                        break;
                }

                ++i;
            }
        }

        public override string ToString()
        {
            bool engineRoom = false;
            int engines = 1;
            StringBuilder sb = new StringBuilder();

            foreach (ShipSystem sys in sysList)
            {
                if (sys.IsEngine)
                {
                    if (engineRoom == false)
                    {
                        sb.Append("(");
                        engineRoom = true;
                        engines = 1;
                    }
                }

                if (sys.IsDestroyed)
                    sb.Append("x");
                else
                    sb.Append(sys.Code);

                if (sys.IsEngine)
                {
                    if (engines == engineSize)
                    {
                        sb.Append(")");
                        engineRoom = false;
                    }
                    else
                        ++engines;
                }
            }

            return sb.ToString();
        }

        private void DamageEngine()
        {
            int undamagedEngines = sysList.Count(sys => sys.IsEngine && !sys.IsDestroyed);

            if (undamagedEngines == 0 && maxSpeed > 0)
            {
                maxSpeed = 0;
                return;
            }

            double oddEngines = undamagedEngines % engineSize;
            double currentMaxSpeed = undamagedEngines / engineSize;

            // If damage has already reduced speed, do nothing.
            if (maxSpeed <= currentMaxSpeed)
                return;

            // If the last engine in a room is destroyed, then set to the new max speed for the remaining.
            if (oddEngines == 0)
            {
                maxSpeed = (int)currentMaxSpeed;
                return;
            }

            // If the roll is greater than the % of remaining engines in the room, reduce the speed, otherwise the speed remains.
            double chance = (oddEngines / engineSize) * 100;
            if (RandomGenerator.Rand(100) > chance)
            {
                maxSpeed = (int)currentMaxSpeed;
            }
        }

        #region ICloneable Members

        protected UnitDetails(UnitDetails unit)
        {
            hull = unit.hull;
            cost = unit.cost;
            maxSpeed = unit.maxSpeed;
            engineSize = unit.engineSize;

            foreach (ShipSystem item in unit.sysList)
                this.sysList.Add(item.Clone() as ShipSystem);
        }

        public object Clone()
        {
            return new UnitDetails(this);
        }

        #endregion
    }
}
